# -*- coding: utf-8 -*-
"""
@Author: BaiZe
@Description: 
@Date: 2025/6/22
"""
import pandas as pd


def compare_csv_files(source_path, target_path, index_columns, encoding='utf-8', max_rows=100_000):
    def _build_rename_mapping(df, index_cols, suffix):
        return {
            f"{col}{suffix}" if col not in index_cols else col: col
            for col in df.columns
        }

    # 验证索引列
    if not index_columns:
        raise ValueError("必须指定至少一个索引列")

    try:
        # 读取CSV文件
        df_source = pd.read_csv(source_path, encoding=encoding, nrows=max_rows, low_memory=False)
        df_target = pd.read_csv(target_path, encoding=encoding, nrows=max_rows, low_memory=False)
    except FileNotFoundError as e:
        raise FileNotFoundError(f"找不到文件: {e.filename}") from e
    except UnicodeDecodeError:
        raise ValueError(f"无法使用编码 '{encoding}' 读取文件，请确认编码格式") from None

    # 确保index_columns为列表格式
    if isinstance(index_columns, str):
        index_columns = [index_columns]

    # 验证索引列存在
    for col in index_columns:
        if col not in df_source.columns:
            raise ValueError(f"索引列 '{col}' 不在源文件中")
        if col not in df_target.columns:
            raise ValueError(f"索引列 '{col}' 不在目标文件中")

    # 检查索引唯一性
    if df_source.duplicated(subset=index_columns).any():
        print("⚠️ 警告: 源文件索引列存在重复值，可能影响比对结果")
    if df_target.duplicated(subset=index_columns).any():
        print("⚠️ 警告: 目标文件索引列存在重复值，可能影响比对结果")

    # 获取非索引列
    non_index_cols = [col for col in df_source.columns if col not in index_columns]

    # 执行全外连接
    merged = pd.merge(
        df_source, df_target,
        on=index_columns,
        how='outer',
        suffixes=('_source', '_target'),
        indicator=True
    )

    # 1. 识别新增行（仅存在于目标文件）
    added_mask = merged['_merge'] == 'right_only'
    added_df = merged[added_mask]
    if not added_df.empty:
        rename_map = _build_rename_mapping(df_target, index_columns, '_target')
        added_df = added_df.rename(columns=rename_map)[df_target.columns]

    # 2. 识别删除行（仅存在于源文件）
    deleted_mask = merged['_merge'] == 'left_only'
    deleted_df = merged[deleted_mask]
    if not deleted_df.empty:
        rename_map = _build_rename_mapping(df_source, index_columns, '_source')
        deleted_df = deleted_df.rename(columns=rename_map)[df_source.columns]

    # 3. 识别修改行（存在于两个文件但有差异）
    both_mask = merged['_merge'] == 'both'
    modified_source = pd.DataFrame(columns=df_source.columns)
    modified_target = pd.DataFrame(columns=df_target.columns)

    if both_mask.any():
        both_df = merged[both_mask]
        # 向量化比较所有非索引列
        change_flags = pd.Series(False, index=both_df.index)
        for col in non_index_cols:
            src_col = col + '_source'
            tgt_col = col + '_target'
            # 比较数值是否不同，或只有一个为NaN
            change_flags |= (
                                    (both_df[src_col] != both_df[tgt_col]) &
                                    ~(both_df[src_col].isna() & both_df[tgt_col].isna())
                            ) | (
                                    both_df[src_col].isna() ^ both_df[tgt_col].isna()
                            )

        modified_indices = both_df[change_flags].index
        if not modified_indices.empty:
            # 源文件版本
            modified_source = both_df.loc[modified_indices][
                [f"{c}_source" if c not in index_columns else c for c in df_source.columns]]
            modified_source.columns = df_source.columns

            # 目标文件版本
            modified_target = both_df.loc[modified_indices][
                [f"{c}_target" if c not in index_columns else c for c in df_target.columns]]
            modified_target.columns = df_target.columns

    return added_df, deleted_df, modified_source, modified_target


# 示例用法
if __name__ == "__main__":
    source = r"C:\Users\Administrator\PycharmProjects\PythonProject\0621\old_data.csv"
    target = r"C:\Users\Administrator\PycharmProjects\PythonProject\0621\new_data.csv"
    index_cols = ["ID"]  # 可以是单列或多列，如 ["ID", "Category"]

    added, deleted, mod_source, mod_target = compare_csv_files(
        source, target, index_cols
    )

    print("=" * 50)
    print("新增行（目标文件中的新条目）:")
    print(added.to_string(index=False) if not added.empty else print("无新增行"))

    print("\n" + "=" * 50)
    print("删除行（源文件中被移除的条目）:")
    print(deleted.to_string(index=False)) if not deleted.empty else print("无删除行")

    print("\n" + "=" * 50)
    print("修改行（源文件中的原始内容）:")
    print(mod_source.to_string(index=False)) if not mod_source.empty else print("无修改行")

    print("\n" + "=" * 50)
    print("修改行（目标文件中的更新内容）:")
    print(mod_target.to_string(index=False)) if not mod_target.empty else print("无修改行")
